﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace cs2335lab5
{
    namespace Client
    {
        /// <summary>
        /// Interaction logic for IssueDetail.xaml
        /// </summary>
        public partial class IssueDetail : Window
        {
            /// <summary>
            /// The issue we're acting on
            /// </summary>
            public Data.Issue Current { get; protected set; }

            /// <summary>
            /// Shortcut to the logged-in user
            /// </summary>
            public Data.User User
            {
                get
                {
                    return App.Instance().User;
                }
            }

            /// <summary>
            /// Accessor for the user list on the server
            /// </summary>
            public Dictionary<string, Data.User> Users
            {
                get
                {
                    lock (App.Instance().Proxy)
                    {
                        return App.Instance().Proxy.GetUserList();
                    }
                }
            }

            /// <summary>
            /// GUI setup method
            /// </summary>
            public IssueDetail()
            {
                InitializeComponent();

                ValidateNameBox();
            }

            /// <summary>
            /// Loads an issue from the server
            /// </summary>
            /// <param name="id"></param>
            public void Load(Int64? id)
            {
                // if we're passed null, just make a new issue
                if (id == null)
                {
                    Current = new Data.Issue();
                }
                else
                {
                    // otherwise ask the server about this issue
                    Current = App.Instance().Proxy.GetIssue(id);

                    // if we're returned null, make a new issue
                    // TODO: may want to notify the user that the local issue is invalid
                    if (Current == null)
                    {
                        Current = new Data.Issue();
                    }
                }

                // implement access rights
                if (Current.ID == null || User.Access == Data.User.Privileges.Admin)
                {
                    // enable all of the fields except for ID, creator, and creation date
                    // issue doesn't exist or user is an admin
                    nameTextBox.IsEnabled = true;           // Current.Name;
                    assignedToBox.IsEnabled = true;     // Current.AssignedTo;
                    creationDateTextBox.IsEnabled = false;   // Current.CreationDate;
                    creatorTextBox.IsEnabled = false;        // Current.Creator;
                    detailedDescriptionTextBox.IsEnabled = true;    // Current.DetailedDescription;
                    dueDateTextBox.IsEnabled = true;        // Current.DueDate;
                    hoursRemainingTextBox.IsEnabled = true; // Current.HoursToGo;
                    hoursWorkedTextBox.IsEnabled = true;    // Current.HoursWorked;
                    //statusTextBox.IsEnabled = true;         // Current.IssueStatus;
                    statusBox.IsEnabled = true;
                }
                else if (Current.Creator.ID.CompareTo(User.ID) == 0 ||
                    Current.AssignedTo.ID.CompareTo(User.ID) == 0)
                {
                    // user either created this issue or 
                    // someone assigned the user to this issue
                    // enable description, status, hoursworked, hoursremaining
                    // disable all other fields
                    nameTextBox.IsEnabled = false;           // Current.Name;
                    assignedToBox.IsEnabled = false;     // Current.AssignedTo;
                    creationDateTextBox.IsEnabled = false;   // Current.CreationDate;
                    creatorTextBox.IsEnabled = false;        // Current.Creator;
                    detailedDescriptionTextBox.IsEnabled = true;    // Current.DetailedDescription;
                    dueDateTextBox.IsEnabled = false;        // Current.DueDate;
                    hoursRemainingTextBox.IsEnabled = true; // Current.HoursToGo;
                    hoursWorkedTextBox.IsEnabled = true;    // Current.HoursWorked;
                    //statusTextBox.IsEnabled = true;         // Current.IssueStatus;
                    statusBox.IsEnabled = true;
                }

                // until we get data binding working, we have to manually 
                // put information into the UI elements
                idTextBox.Text = (Current.ID != null) ? Current.ID.ToString() : "";
                nameTextBox.Text = Current.Name;
                detailedDescriptionTextBox.Text = Current.DetailedDescription;
                
                // for the dates, if the value is the minvalue, display an empty string
                creationDateTextBox.Text = (Current.CreationDate == DateTime.MinValue) ? "" : 
                    Current.CreationDate.ToShortDateString() + " " + Current.CreationDate.ToShortTimeString();
                dueDateTextBox.Text = (Current.DueDate == DateTime.MinValue) ? "" :
                    Current.DueDate.ToShortDateString() + " " + Current.DueDate.ToShortTimeString();

                // go ahead and fill out the creator text box with the user's info if ID is null
                creatorTextBox.Text = (Current.ID == null) ? User.ID : 
                    ((Current.Creator == null) ?  "" : Current.Creator.ID);

                assignedToBox.Text = (Current.AssignedTo != null) ? Current.AssignedTo.ID : "";
                //statusTextBox.Text = (Current.IssueStatus == null) ? "" : 
                    //Enum.GetName(typeof(Data.Issue.Status), Current.IssueStatus);
                statusBox.Text = (Current.IssueStatus == null) ? "" :
                    Enum.GetName(typeof(Data.Issue.Status), Current.IssueStatus);
                hoursWorkedTextBox.Text = Current.HoursWorked.ToString();
                hoursRemainingTextBox.Text = Current.HoursToGo.ToString();

                // check the status of the name box, enable/disable OK/Apply accordingly
                ValidateNameBox();
            }

            /// <summary>
            /// This method takes all of the data from the form
            /// and tries to send it to the server
            /// </summary>
            private void ParseAndSaveData()
            {
                // TODO: DATA VALIDATION, BITCHES
                // Might want to parse the date after the user enters it; 
                // if a field can't parse, highlight the control in red or something

                // TODO: make date due into a date picker (or possibly a date/time picker
                // TODO: make assigned to a dropdown box with a list of users
                // TODO: admins can select from all users, normals can only select themselves
                // TODO: make status a dropdown box of statuses

                // read in the fields and assign them to variables until we get data binding working
                if (Current == null)
                {
                    Current = new Data.Issue();
                }

                // assign fields
                Current.Name = nameTextBox.Text;
                Current.DetailedDescription = detailedDescriptionTextBox.Text;
                try
                {
                    Current.DueDate = Convert.ToDateTime(dueDateTextBox.Text);
                }
                catch (FormatException)
                {
                    Current.DueDate = DateTime.MinValue;
                }
                if (assignedToBox.Text != null && assignedToBox.Text.CompareTo("") != 0)
                {
                    Current.AssignedTo = App.Instance().Proxy.GetUser(assignedToBox.Text);
                }
                try
                {
                    Current.IssueStatus = (Data.Issue.Status)Enum.Parse(typeof(Data.Issue.Status), statusBox.Text);
                }
                catch (ArgumentException)
                {
                    Current.IssueStatus = null;
                }
                try
                {
                    Current.HoursWorked = Convert.ToDecimal(hoursWorkedTextBox.Text);
                    Current.HoursToGo = Convert.ToDecimal(hoursRemainingTextBox.Text);
                }
                catch (FormatException)
                {
                }

                // take the current state of the form and update the issue on the server
                if (Current.ID == null)
                {
                    Current = App.Instance().Proxy.AddIssue(User, Current);
                }
                else
                {
                    Current = App.Instance().Proxy.UpdateIssue(User, Current);
                }
            }

            /// <summary>
            /// Save the data but don't exit
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Apply_Click(object sender, RoutedEventArgs e)
            {
                // send the data to the server
                ParseAndSaveData();

                // notify the ClientForm it needs to update
                App.Instance().UpdateClient();

                // update the window
                if(Current != null && Current.ID != null)
                {
                    Load(Current.ID);
                }
            }

            /// <summary>
            /// Save button event handler
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OK_Click(object sender, RoutedEventArgs e)
            {
                // send the data to the server
                ParseAndSaveData();

                // notify the ClientForm it needs to update
                App.Instance().UpdateClient();

                // hide the window
                Cancel_Click(sender, e);
            }

            /// <summary>
            /// Cancel button event handler
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Cancel_Click(object sender, RoutedEventArgs e)
            {
                // clear the data in the form and hide it
                Current = new Data.Issue();
                this.Hide();
            }

            /// <summary>
            /// When the window is closed, just hide it
            /// code from http://balajiramesh.wordpress.com/2008/07/24/hide-a-window-instead-of-closing-it-in-wpf/
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
            {
                // Hide Window
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
                    (DispatcherOperationCallback)delegate(object o)
                {
                    Hide();
                    return null;
                }, null);

                // Do not close window
                e.Cancel = true; 
            }

            /// <summary>
            /// Some code to prevent text input in Hours Worked other than numeric digits, from:
            /// http://dotnetus.spaces.live.com/Blog/cns!4E39ECD492E4EEC1!550.entry?wa=wsignin1.0&sa=454526412
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void hoursWorkedTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
            {
                foreach (char c in e.Text)
                {
                    if (!Char.IsDigit(c))
                    {
                        e.Handled = true;
                        break;
                    }
                }
                base.OnPreviewTextInput(e);
            }

            /// <summary>
            /// Some code to prevent text input in Hours Remaining other than numeric digits, from:
            /// http://dotnetus.spaces.live.com/Blog/cns!4E39ECD492E4EEC1!550.entry?wa=wsignin1.0&sa=454526412
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void hoursRemainingTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
            {
                foreach (char c in e.Text)
                {
                    if (!Char.IsDigit(c))
                    {
                        e.Handled = true;
                        break;
                    }
                }
                base.OnPreviewTextInput(e);
            }

            /// <summary>
            /// Repopulate the combo box
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void assignedToBox_DropDownOpened(object sender, EventArgs e)
            {
                // clear the current items
                assignedToBox.Items.Clear();

                // if the user isn't an admin, the only choice is the user's username
                if (User.Access == Data.User.Privileges.Normal)
                {
                    assignedToBox.Items.Add(User.ID);
                }
                else
                {
                    // otherwise the user can choose from any available user
                    foreach (KeyValuePair<string, Data.User> usersLine in Users)
                    {
                        assignedToBox.Items.Add(usersLine.Key);
                    }
                }
            }

            /// <summary>
            /// Our only real validation is the name box; the rest is generally optional.
            /// pass the call onto the function for this purpose
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void nameTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                ValidateNameBox();
            }

            /// <summary>
            /// If there isn't a name box or if it's empty, disable the OK/Apply buttons
            /// Set the appropriate tool tips
            /// </summary>
            private void ValidateNameBox()
            {
                // if there is no name
                if (nameTextBox == null || nameTextBox.Text == null || nameTextBox.Text.Equals(""))
                {
                    // then the user can't click OK or Apply
                    Apply.IsEnabled = false;
                    Apply.ToolTip = "Cannot save issue with a blank name.";
                    OK.IsEnabled = false;
                    OK.ToolTip = "Cannot save issue with a blank name.";

                    // highlight the box
                    nameTextBox.Background = new SolidColorBrush(Colors.LightCoral);
                }
                else
                {
                    // otherwise they can be
                    Apply.IsEnabled = true;
                    Apply.ToolTip = "Save issue and keep dialog open";
                    OK.IsEnabled = true;
                    OK.ToolTip = "Save issue and close dialog";

                    // unhighlight the box
                    nameTextBox.Background = new SolidColorBrush(Colors.White);
                }
            }
        }
    }
}